package com.haoran.dwz.common.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import com.alibaba.fastjson.JSON;
import com.haoran.dwz.rest.R;
import com.haoran.dwz.rest.ReturnCode;

/**
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig  {

	@Autowired
	private JwtAuthenticationEntryPoint authenticationEntryPoint;

	@Autowired
	private UsmSecurityInterceptorFilter usmSecurityInterceptorFilter;

	@Autowired
	private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

	@Autowired
	private UsmInitializationFilter usmInitializationFilter;

	@Autowired
	private ApiPathPattern apiPathPattern;
 
	
	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth,UserDetailsService userDetailsService,PasswordEncoder passwordEncoder) throws Exception {
		auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
	}
	
	@Autowired
	private SessionRegistry sessionRegistry;


	@Autowired
	private PersistentTokenRepository tokenRepository;



	private String key = "weixin:javajidi_com";

	@Autowired
	private UsmAuthenticationSuccessHandler successHandler;
	
	@Autowired
	private UsmAuthenticationFailureHandler failureHandler;

	@Configuration
	@Order(1)
	public class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
		
		@Override
		public void configure(WebSecurity web) throws Exception {
			web.ignoring().antMatchers("/static/**","/statics/**");
		}
		
		protected void configure(HttpSecurity httpSecurity) throws Exception {
			
			httpSecurity.csrf().ignoringAntMatchers("/static/**","/statics/**").disable();// 既然用jwt就必须有这个
			httpSecurity.cors();
			httpSecurity.authorizeRequests().antMatchers(apiPathPattern.getPublicPathPatterns()).permitAll();
			httpSecurity.authorizeRequests().antMatchers(apiPathPattern.getAclPathPattern()).permitAll();
			httpSecurity.antMatcher(apiPathPattern.getSecurityPathPatterns()).authorizeRequests().antMatchers(apiPathPattern.getSecurityPathPatterns()).authenticated();
			httpSecurity.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and();
			httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and();
			httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
			httpSecurity.addFilterBefore(usmSecurityInterceptorFilter, FilterSecurityInterceptor.class);
			httpSecurity.httpBasic().and().headers().cacheControl();
		}
	}

	/**
	 * 数据库管理员登录
	 */
	@Configuration
	@Order(10)
	public class DBWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
		protected void configure(HttpSecurity http) throws Exception {
			http.csrf().disable();// 这个想办法去掉这个
			http.antMatcher("/h2-console/**").authorizeRequests().anyRequest().hasAuthority("role_dba").and().headers()
					.frameOptions().sameOrigin();
		}
	}

	@Configuration
	@Order(20)
	public class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

		@Override
		public void configure(WebSecurity web) throws Exception {
			web.ignoring().antMatchers(apiPathPattern.getAclPathPattern(),"/static/**", "/statics/**","/dist/**", "/**/*.js", "/**/*.js.map", "/**/*.ts", "/**/*.css", "/**/*.css.map",
					"/**/*.png", "/**/*.gif", "/**/*.jpg", "/**/*.fco", "/**/*.woff", "/**/*.woff2", "/**/*.font",
					"/**/*.svg", "/**/*.ttf", "/**/*.pdf", "/*.ico", "/404", "/401", "/403", "/error", "/auth/**",
					"/portal/**", "/css/**", "/js/**", "/except/**",apiPathPattern.getSecurityPathPatterns());
		}

		@Override
		protected void configure(HttpSecurity httpSecurity) throws Exception {
			failureHandler.setDefaultFailureUrl("/login");
			httpSecurity.exceptionHandling().authenticationEntryPoint(new MyAuthenticationEntryPoint())
					.accessDeniedHandler(new MyAccessDeniedHandler());
		
			httpSecurity.authorizeRequests().antMatchers("/swagger-resources/**","/swagger-ui.html","/v2/api-docs").permitAll();
			httpSecurity.authorizeRequests().antMatchers("/auth/**", "/install", "/init", "/except/**").permitAll();
			httpSecurity.authorizeRequests().anyRequest().authenticated().and().formLogin().loginPage("/login")
					.loginProcessingUrl("/login").successHandler(successHandler).failureHandler(failureHandler).permitAll().and().headers()
					.frameOptions().sameOrigin()// 表示该页面可以在相同域名页面的
												// frame 中展示
					.and().sessionManagement().invalidSessionUrl("/login").maximumSessions(1).expiredUrl("/expired")
					.sessionRegistry(sessionRegistry).and().and().logout().permitAll().logoutSuccessUrl("/login")
					.invalidateHttpSession(true).and().rememberMe().key(key).tokenValiditySeconds(7 * 24 * 60 * 60)
					// 指定记住登录信息所使用的数据源
					.tokenRepository(tokenRepository);
			httpSecurity.addFilterBefore(usmSecurityInterceptorFilter, FilterSecurityInterceptor.class);
			httpSecurity.addFilterBefore(usmInitializationFilter, ChannelProcessingFilter.class);
			if(usmInitializationFilter.isVerificationCode()) {
				httpSecurity.addFilterBefore(new KaptchaAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
			}
		}
	}

	public boolean isAjax(HttpServletRequest request) {
		return StringUtils.isNotBlank(request.getHeader("X-Requested-With"));
	}

	public ApiPathPattern getApiPathPattern() {
		return apiPathPattern;
	}

	public void setApiPathPattern(ApiPathPattern apiPathPattern) {
		this.apiPathPattern = apiPathPattern;
	}

	private class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {

		@Override
		public void commence(HttpServletRequest request, HttpServletResponse response,
				AuthenticationException authException) throws IOException {
			response.setCharacterEncoding("utf-8");
			if (isAjax(request)) {
				response.setContentType("application/json");
				response.getWriter().println(JSON.toJSONString(R.result(ReturnCode.NOT_LOGGED_IN_ERROR)));
			} else {
				response.sendRedirect("/login");
			}

		}
	}

	private class MyAccessDeniedHandler implements AccessDeniedHandler {
		@Override
		public void handle(HttpServletRequest request, HttpServletResponse response,
				AccessDeniedException accessDeniedException) throws IOException, ServletException {
			response.setCharacterEncoding("utf-8");
			if (isAjax(request)) {
				response.setContentType("application/json");
				response.getWriter().println(JSON.toJSONString(R.result(ReturnCode.UNAUTHORIZED_ERROR)));
			} else {
				response.sendRedirect("/403");
			}

		}
	}
}
